﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JigLibX.Physics;
using JigLibX.Collision;
using JigLibX.Geometry;
using JigLibX.Math;
using JigLibX.Utils;
using Microsoft.Xna.Framework;

namespace DARE
{
    public class CRayScanner
    {

    #region fields

        private CRender3D m_render;

        private Vector3 m_position;
        private Vector3 m_direction;
        private bool m_directionNeedUpdate;

        private int m_scanWidth;
        private int m_scanHeight;
        private bool m_sizeNeedUpdate;
        private int m_rayNumber;

        private float m_scanStep;
        private int m_scanDepth;

        private Vector3 m_localX;
        private Vector3 m_localY;

        private CRayCast.CIntersectInfo[,] m_scanMatrice;
        private const float m_minScan = 0.001f;

    #endregion

    #region properties

        public Vector3 Position
        {
            get { return m_position; }
            set { m_position = value; }
        }

        public Vector3 Direction
        {
            get { return m_direction; }
            set
            {
                if (m_direction != value)
                {
                    m_direction = value;
                    m_directionNeedUpdate = true;
                }
            }
        }

        public int ScanWidth
        {
            get { return m_scanWidth; }
            set 
            { 
                if (m_scanWidth != value)
                {
                    m_scanWidth = value;
                    m_sizeNeedUpdate = true;
                }
            }
        }

        public int ScanHeight
        {
            get { return m_scanHeight; }
            set 
            { 
                if (m_scanHeight != value)
                {
                    m_scanHeight = value;
                    m_sizeNeedUpdate = true;
                }
            }
        }

        public int RayNumber
        {
            get { return m_rayNumber; }
        }

        public float ScanStep
        {
            get { return m_scanStep; }
            set
            {
                if (value >= m_minScan)
                    m_scanStep = value;
            }
        }

        public int ScanDepth
        {
            get { return m_scanDepth; }
            set
            {
                if (value >= 0)
                    m_scanDepth = value;
            }
        }

        public CRayCast.CIntersectInfo[,] ScanMatrice
        {
            get { return m_scanMatrice; }
        }

    #endregion

    #region ctor

        public CRayScanner(CRender3D render)
        {
            Initialize(render, Vector3.UnitY * 20.0f, -Vector3.UnitY, 4, 10, 1.0f);
        }

        private void Initialize(CRender3D render, Vector3 position, Vector3 direction, int scanHeight, int scanWidth, float scanStep)
        {
            if (render == null)
                throw new Exception("The render cannot be null !");
            m_render = render;
            ScanDepth = 100;
            Position = position;
            Direction = direction;
            ScanHeight = scanHeight;
            ScanWidth = scanWidth;
            ScanStep = scanStep;
            m_rayNumber = ScanWidth * ScanHeight;
        }

    #endregion

    #region methods

        public CRayCast.CIntersectInfo[,] ProcessScan()
        {
            // if direction changed we calculate the new local axis
            if (m_directionNeedUpdate == true)
            {
                m_direction.Normalize();
                //m_localX = Vector3.Transform(m_direction, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationY(MathHelper.PiOver2)));
                //m_localY = Vector3.Transform(m_direction, Quaternion.CreateFromRotationMatrix(Matrix.CreateRotationX(MathHelper.PiOver2)));
                //m_localY = Vector3.Cross(m_localX, m_direction);
                m_localX = Vector3.UnitX;
                m_localY = -Vector3.UnitZ;
                m_directionNeedUpdate = false;
            }
            // if the scan size changed we rebuild the scan matrice
            if (m_sizeNeedUpdate == true)
            {
                m_scanMatrice = new CRayCast.CIntersectInfo[m_scanHeight, m_scanWidth];
                m_rayNumber = m_scanHeight * m_scanWidth;
                m_sizeNeedUpdate = false;
            }
            // process the scan
            Vector3 posHeight, posWidth, rayPos;
            //CDare.Instance.DebugMgr.DebugConsole.WriteLine("position: " + m_position.ToString() + " direction: " + m_direction.ToString() + 
                //"\nlocalX: " + m_localX.ToString() + "  localY: " + m_localY.ToString());
            for (int height = 0; height < m_scanHeight; ++height)
            {
                posHeight = m_localY * ((float)height - (float)m_scanHeight / 2.0f);
                for (int width = 0; width < m_scanWidth; ++width)
                {
                    posWidth = m_localX * ((float)width - (float)m_scanWidth / 2.0f);
                    rayPos = m_scanStep * (posWidth + posHeight) + m_position;
                    m_scanMatrice[height, width] = m_render.RayCast.IntersectFirst(rayPos, m_direction * m_scanDepth, m_scanDepth);
                    // -----------------------------------------------------------------------------------------------------
                    //CDare.Instance.DebugMgr.DebugConsole.WriteLine("posHeight: " + posHeight.ToString() + " posWidth: " + posWidth.ToString() + 
                    //    "\nscan[" + height.ToString() + ", " + width.ToString() + "] : pos: " + rayPos.ToString());
                    //CRayCast.CIntersectInfo info = m_render.RayCast.IntersectFirst(rayPos, m_direction, m_scanDepth);
                    //if (info == null)
                    //    CDare.Instance.DebugMgr.DebugConsole.WriteLine("info is null !");
                    //else
                    //    CDare.Instance.DebugMgr.DebugConsole.WriteLine(info.node.Name);
                    // -----------------------------------------------------------------------------------------------------
                }
            }
            return m_scanMatrice;
        }

        public CRayCast.CIntersectInfo[,] ProcessScan(Vector3 position, Vector3 direction)
        {
            Position = position;
            Direction = direction;
            return ProcessScan();
        }

        public CRayCast.CIntersectInfo[,] ProcessScan(Vector3 position, Vector3 direction, int scanHeight, int scanWidth)
        {
            Position = position;
            Direction = direction;
            ScanHeight = scanHeight;
            ScanWidth = scanWidth;
            return ProcessScan();
        }

    #endregion

    }
}
